package impl

import (
	"context"
	"time"

	"gitee.com/go-course/go9/tree/master/projects/vblog/api/apps/blog"
	"gitee.com/go-course/go9/tree/master/projects/vblog/api/apps/tag"
	"github.com/imdario/mergo"
)

// 如果用户取消了请求，这个数据还会创建吗?
// ctx.Done()
func (i *impl) CreateBlog(ctx context.Context, req *blog.CreateBlogRequest) (
	*blog.Blog, error) {
	// 1. 请求参数的校验, 如何进行参数校验
	if err := req.Validate(); err != nil {
		return nil, err
	}

	// 2. 通过用户请求参数构建一个对象
	//    2.1: 需要实现一个构造函数, 用于构造Blog
	//    2.2：能不能不用构造函数
	// 问题? 由很多地方都需要写着构造, 导致代码写很多遍
	// 使用构造函数 来对对象进行统一的维护
	// ins := &blog.Blog{
	// 	CreatedAt:         time.Now().Unix(),
	// 	CreateBlogRequest: req,
	// 	Meta:              map[string]string{},
	// }
	ins := blog.NewBlog(req)
	// 3. 把对象保存到数据库, Mysql, 需要操作数据库
	// DB 不是指的一个连接，指的一个连接池
	// 可以通过 sql.DB 这个连接池来与数据库进行交互
	// INSERT INTO `blogs` (`created_at`,`updated_at`,`pulished_at`,`title`,`author`,`summary`,`content`,`status`) VALUES (1669445556,1669445556,0,'Go Course1','oldfish','Go Javascript','baba',0)
	if err := i.db.WithContext(ctx).Save(ins).Error; err != nil {
		return nil, err
	}

	// 依赖Tag具体实例来进行 标签创建
	// i.tag.CreateTag()

	// 4. 返回对象
	return ins, nil
}

func (i *impl) QueryBlog(ctx context.Context, req *blog.QueryBlogRequest) (
	*blog.BlogSet, error) {
	query := i.db.Table("blogs").WithContext(ctx)

	// 条件组装，数据筛选
	if req.Author != "" {
		// ? 是占位符, sql prepare, req.Auth="drap database"
		// gorm 是链式调用, 并且值copy的方式
		query = query.Where("author = ?", req.Author)
	}
	if req.Status != nil {
		query = query.Where("status = ?", *req.Status)
	}
	if req.Keywords != "" {
		// SQL LIKE 字符串通配, %: 代表任意字符: %Go% 凡是字符串里面包含Go
		// title LIKE ? OR author LIKE ?
		query = query.Where("title LIKE ?", "%"+req.Keywords+"%")
	}

	// 进行数据的分页查询, SLQ  LIMIT 来实现分页  LIMIT offset,limit  LIMIE 0,10 1-10 LIMIT 10,10  11-20
	// limit 语法转换成 page 怎么转换?
	set := blog.NewBlogSet()
	err := query.
		Order("created_at DESC").
		Offset(int(req.Offset())).
		Limit(int(req.PageSize)).
		Find(&set.Items).
		Error
	if err != nil {
		return nil, err
	}

	tagReq := tag.NewQueryTagRequest()
	// 依赖Tag具体实例来进行 标签的补充
	// 有多少个就需要For循环多个词,
	// TODO(作业): 可以使用批量进行处理, 需要过滤出每个blog的Tags 进行Blog更新
	for index := range set.Items {
		// 每个Blog, 补充Tag
		item := set.Items[index]
		tagReq.BlogIds = []int{item.Id}
		ts, err := i.tag.QueryTag(ctx, tagReq)
		if err != nil {
			return nil, err
		}
		item.Tags = ts.Items
	}

	// 统计总数量
	err = query.Count(&set.Total).Error
	if err != nil {
		return nil, err
	}

	return set, nil
}

func (i *impl) DescribeBlog(ctx context.Context, req *blog.DescribeBlogRequest) (
	*blog.Blog, error) {
	// 依赖Tag具体实例来进行 标签创建

	query := i.db.Table("blogs").WithContext(ctx)

	ins := blog.NewBlog(blog.NewCreateBlogRequest())
	err := query.Where("id = ?", req.Id).First(ins).Error
	if err != nil {
		return nil, err
	}

	tagReq := tag.NewQueryTagRequest()
	tagReq.BlogIds = []int{ins.Id}
	ts, err := i.tag.QueryTag(ctx, tagReq)
	if err != nil {
		return nil, err
	}
	ins.Tags = ts.Items
	return ins, nil
}

func (i *impl) UpdateBlog(ctx context.Context, req *blog.UpdateBlogRequest) (
	*blog.Blog, error) {
	// 依赖Tag具体实例来进行 标签创建
	// i.tag.CreateTag()

	// 查询之前的对象
	ins, err := i.DescribeBlog(ctx, blog.NewDescribeBlogRequest(req.Id))
	if err != nil {
		return nil, err
	}

	// 修改对象
	switch req.UpdateMode {
	case blog.PUT:
		// 全量更新, 原来的值完全不要了
		ins.CreateBlogRequest = req.CreateBlogRequest
	case blog.PATCH:
		// 判断是否是发布动作
		if ins.Status != req.Status && req.Status == blog.PULISHED {
			ins.PulishedAt = time.Now().Unix()
		}

		// 局部更新, merge 操作, 如果用户没有传递更新字段，该字段是不更新的
		// if req.Author != "" {
		// 	ins.Author = req.Author
		// }
		// if req.Title != "" {
		// 	ins.Title = req.Title
		// }
		// Object Merge  Old <--Patch- New
		// https://github.com/imdario/mergo
		err := mergo.MergeWithOverwrite(ins.CreateBlogRequest, req.CreateBlogRequest)
		if err != nil {
			return nil, err
		}
	}

	// 如果你修改过后数据不对, title == "", 保证修改后的数据合法
	if err := ins.CreateBlogRequest.Validate(); err != nil {
		return nil, err
	}

	err = i.db.
		Table("blogs").
		WithContext(ctx).
		Where("id = ?", req.Id).
		Updates(ins).
		Error
	if err != nil {
		return nil, err
	}

	return ins, nil
}

func (i *impl) DeleteBlog(ctx context.Context, req *blog.DeleteBlogRequest) (
	*blog.Blog, error) {
	// 依赖Tag具体实例来进行 标签创建
	// i.tag.CreateTag()

	// 查询之前的对象
	ins, err := i.DescribeBlog(ctx, blog.NewDescribeBlogRequest(req.Id))
	if err != nil {
		return nil, err
	}

	err = i.db.
		Table("blogs").
		WithContext(ctx).
		Delete(&blog.Blog{Id: req.Id}).
		Error
	if err != nil {
		return nil, err
	}

	return ins, nil
}
